package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
)

// 声明结构体
type user struct {
	id   int
	name string
	age  int
}

// 定义全局变量数据库连接池db
var db *sql.DB //是一个连接池
func initDB() (err error) {
	// 数据库的信息
	dsn := "root:123@tcp(127.0.0.1:3306)/sql_test"
	// 连接数据库
	db, err = sql.Open("mysql", dsn) //不会校验用户名和密码是否正确.  sql.Opne("数据库类型",数据库信息)
	if err != nil {                  //dsn格式不正确的时候会报错
		return
	}
	err = db.Ping() //这才是真正尝试连接数据库
	if err != nil {
		return
	}
	// 设置数据库连接池的最大连接数
	db.SetMaxOpenConns(100)
	// 设置最大空闲连接数
	db.SetMaxIdleConns(10)
	return
}

// 查询单条数据 数据库示例   单行查询db.QueryRow()单行查询
func queryOne(id int) {
	// 1.写查询单条记录的sql语句
	var u1 user
	sqlStr := `select id,name,age from user where id=?;` //静态查询。直接指定死查询的id   动态查询，将id=1换为id=？，然后再执行语句中指定查询的id号
	// 2.执行
	rowObj := db.QueryRow(sqlStr, id) //从连接池里拿一个连接出来去数据库查询单条记录
	// 3.拿到结果
	rowObj.Scan(&u1.id, &u1.name, &u1.age) //必须对rowObj调用Scan方法，因为该方法会释放数据库的连接
	// 4.打印结果
	fmt.Printf("u1:%#v\n", u1)
}

// 查询多行数据 db.query()
func queryMore(n int) {
	// 1.要查询的sql语句
	sqlStr := `select id,name,age from user where id > ?;`
	// 2.执行sql语句
	rows, err := db.Query(sqlStr, n)
	if err != nil {
		fmt.Printf("exec %s query failed,err:%v\n", sqlStr, err)
		return
	}
	// 3,一定要关闭rows ，rows返回的是1个结果集
	defer rows.Close()
	// 4.循环取值
	for rows.Next() { //多行数据通过写一个for循环来进行处理，有数据返回true接着执行，没数据返回false就退出 Next就如同一个游标，查到最后就会返回一个false，就查询结束退出循环了。
		var u1 user
		err := rows.Scan(&u1.id, &u1.name, &u1.age)
		if err != nil {
			fmt.Printf("scan fialed,err:%v\n", err)
			return
		}
		fmt.Printf("u1:%#v\n", u1)
	}
}

// 插入数据 （插入、删除、更新都是采用Exec()方法）
func insertsql() {
	// 1.写要插入的sql语句(写死的sql语句)
	sqlStr := `insert into user(name,age)values("yly",22);`
	// 2.执行要插入的sql语句
	ret, err := db.Exec(sqlStr)
	if err != nil {
		fmt.Printf("insert failed,err:%v\n", err)
		return
	}
	// 如果是插入数据的操作，能够拿到插入数据的id(此处就相当于验证sql是否插入成功，因为数据库中id为自增的)
	id, err := ret.LastInsertId() //LastInsertId() 插入语句的主键id获取
	if err != nil {
		fmt.Printf("get id failed,err:%v\n", err)
		return
	}
	fmt.Println("get id success,id: ", id)
}

// 更新sql
func updatesql(newAge int, id int) {
	// 1.要更新的sql语句
	sqlStr := `update user set age=? where id =?;`
	// 2.输入要更新的id和值
	ret, err := db.Exec(sqlStr, newAge, id)
	if err != nil {
		fmt.Printf("update failed ,err:%v\n", err)
		return
	}
	// 检查是否更新成功
	n, err := ret.RowsAffected() //此次操作影响的行数
	if err != nil {
		fmt.Printf("get id failed ,err:%v\n", err)
		return
	}
	fmt.Println("get id success,id: ", n)
}

// 删除sql
func deletesql(id int) {
	sqlStr := `delete from user where id=?;`
	ret, err := db.Exec(sqlStr, id)
	if err != nil {
		fmt.Printf("delete sql failed,err:%v\n", err)
		return
	}
	n, err := ret.RowsAffected()
	if err != nil {
		fmt.Printf("delete sql failed id ,err:%v\n", err)
		return
	}
	fmt.Println("delete sql success id ,id: ", n)
}

// go连接mysql示例
func main() {
	err := initDB()
	if err != nil {
		fmt.Printf("init db failed,err:%v\n", err)
	}
	fmt.Println("数据库连接成功")
	queryOne(2)
	queryMore(0)
	insertsql()
	updatesql(24, 3)
	deletesql(2)
}
